"""
Flask认证示例 - 展示JWT认证、权限控制和用户管理
包含用户注册、登录、权限验证、密码重置等功能
"""

from flask import Flask, request, jsonify, g
import os
import sys
import time
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, List
from functools import wraps

# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from python_backend_toolkit.auth import (
    JWTAuth, PasswordManager, TokenManager, 
    AuthService, Permission, Role
)
from python_backend_toolkit.logging import get_logger
from python_backend_toolkit.utils import generate_uuid, validate_email
from python_backend_toolkit.api.responses import SuccessResponse, ErrorResponse
from python_backend_toolkit.api.exceptions import UnauthorizedError, ForbiddenError


def create_auth_app() -> Flask:
    """创建认证示例Flask应用"""
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'auth-demo-secret-key-change-in-production'
    
    # 初始化认证组件
    jwt_auth = JWTAuth(
        secret_key=app.config['SECRET_KEY'],
        algorithm='HS256',
        access_token_expire=timedelta(hours=1),
        refresh_token_expire=timedelta(days=7)
    )
    
    password_manager = PasswordManager()
    token_manager = TokenManager()
    auth_service = AuthService(jwt_auth, password_manager)
    logger = get_logger("AuthApp")
    
    # 模拟用户数据库
    MOCK_USERS = {
        1: {
            "id": 1,
            "username": "admin",
            "email": "admin@example.com",
            "password_hash": password_manager.hash_password("admin123"),
            "role": "admin",
            "permissions": ["read", "write", "delete", "admin"],
            "is_active": True,
            "created_at": datetime.utcnow() - timedelta(days=30),
            "last_login": None
        },
        2: {
            "id": 2,
            "username": "user1",
            "email": "user1@example.com",
            "password_hash": password_manager.hash_password("user123"),
            "role": "user",
            "permissions": ["read", "write"],
            "is_active": True,
            "created_at": datetime.utcnow() - timedelta(days=15),
            "last_login": None
        },
        3: {
            "id": 3,
            "username": "viewer",
            "email": "viewer@example.com",
            "password_hash": password_manager.hash_password("viewer123"),
            "role": "viewer",
            "permissions": ["read"],
            "is_active": True,
            "created_at": datetime.utcnow() - timedelta(days=7),
            "last_login": None
        }
    }
    
    # 用户ID计数器
    USER_ID_COUNTER = max(MOCK_USERS.keys()) + 1
    
    # 黑名单令牌存储
    BLACKLISTED_TOKENS = set()
    
    @app.before_request
    def before_request():
        """请求前处理"""
        g.request_id = generate_uuid()
        g.start_time = time.time()
        g.current_user = None
    
    @app.after_request
    def after_request(response):
        """请求后处理"""
        duration = time.time() - g.start_time
        user_info = f"user_id={g.current_user['id']}" if g.current_user else "anonymous"
        
        logger.info(
            "Request completed",
            request_id=g.request_id,
            method=request.method,
            path=request.path,
            status_code=response.status_code,
            duration=duration,
            user=user_info
        )
        return response
    
    # ========================================================================
    # 认证装饰器
    # ========================================================================
    
    def token_required(f):
        """需要有效令牌的装饰器"""
        @wraps(f)
        def decorated(*args, **kwargs):
            token = None
            auth_header = request.headers.get('Authorization')
            
            if auth_header:
                try:
                    token = auth_header.split(' ')[1]  # Bearer <token>
                except IndexError:
                    return jsonify(ErrorResponse.create(
                        message="Invalid authorization header format"
                    ).dict()), 401
            
            if not token:
                return jsonify(ErrorResponse.create(
                    message="Token is missing"
                ).dict()), 401
            
            if token in BLACKLISTED_TOKENS:
                return jsonify(ErrorResponse.create(
                    message="Token has been revoked"
                ).dict()), 401
            
            try:
                payload = jwt_auth.decode_token(token)
                user_id = payload.get('user_id')
                
                if not user_id or user_id not in MOCK_USERS:
                    return jsonify(ErrorResponse.create(
                        message="Invalid token"
                    ).dict()), 401
                
                user = MOCK_USERS[user_id].copy()
                user.pop('password_hash', None)  # 不返回密码哈希
                g.current_user = user
                
            except Exception as e:
                return jsonify(ErrorResponse.create(
                    message=f"Token validation failed: {str(e)}"
                ).dict()), 401
            
            return f(*args, **kwargs)
        return decorated
    
    def permission_required(permission: str):
        """需要特定权限的装饰器"""
        def decorator(f):
            @wraps(f)
            @token_required
            def decorated(*args, **kwargs):
                if permission not in g.current_user.get('permissions', []):
                    return jsonify(ErrorResponse.create(
                        message=f"Permission '{permission}' required"
                    ).dict()), 403
                return f(*args, **kwargs)
            return decorated
        return decorator
    
    def role_required(role: str):
        """需要特定角色的装饰器"""
        def decorator(f):
            @wraps(f)
            @token_required
            def decorated(*args, **kwargs):
                if g.current_user.get('role') != role:
                    return jsonify(ErrorResponse.create(
                        message=f"Role '{role}' required"
                    ).dict()), 403
                return f(*args, **kwargs)
            return decorated
        return decorator
    
    # ========================================================================
    # 用户注册和登录
    # ========================================================================
    
    @app.route('/auth/register', methods=['POST'])
    def register():
        """用户注册"""
        nonlocal USER_ID_COUNTER
        
        data = request.get_json()
        username = data.get('username', '').strip()
        email = data.get('email', '').strip()
        password = data.get('password', '')
        role = data.get('role', 'user')
        
        # 验证输入
        if not username or len(username) < 3:
            return jsonify(ErrorResponse.create(
                message="Username must be at least 3 characters long"
            ).dict()), 400
        
        if not validate_email(email):
            return jsonify(ErrorResponse.create(
                message="Invalid email format"
            ).dict()), 400
        
        if len(password) < 6:
            return jsonify(ErrorResponse.create(
                message="Password must be at least 6 characters long"
            ).dict()), 400
        
        if role not in ['user', 'viewer']:
            return jsonify(ErrorResponse.create(
                message="Invalid role. Only 'user' and 'viewer' are allowed for registration"
            ).dict()), 400
        
        # 检查用户名和邮箱是否已存在
        for user in MOCK_USERS.values():
            if user['username'] == username:
                return jsonify(ErrorResponse.create(
                    message="Username already exists"
                ).dict()), 409
            if user['email'] == email:
                return jsonify(ErrorResponse.create(
                    message="Email already exists"
                ).dict()), 409
        
        # 创建新用户
        user_id = USER_ID_COUNTER
        USER_ID_COUNTER += 1
        
        permissions = ["read", "write"] if role == "user" else ["read"]
        
        new_user = {
            "id": user_id,
            "username": username,
            "email": email,
            "password_hash": password_manager.hash_password(password),
            "role": role,
            "permissions": permissions,
            "is_active": True,
            "created_at": datetime.utcnow(),
            "last_login": None
        }
        
        MOCK_USERS[user_id] = new_user
        
        # 返回用户信息（不包含密码）
        user_response = new_user.copy()
        user_response.pop('password_hash')
        user_response['created_at'] = user_response['created_at'].isoformat()
        
        response = SuccessResponse.create(
            data={"user": user_response},
            message="User registered successfully"
        )
        return jsonify(response.dict()), 201
    
    @app.route('/auth/login', methods=['POST'])
    def login():
        """用户登录"""
        data = request.get_json()
        username = data.get('username', '').strip()
        password = data.get('password', '')
        
        if not username or not password:
            return jsonify(ErrorResponse.create(
                message="Username and password are required"
            ).dict()), 400
        
        # 查找用户（支持用户名或邮箱登录）
        user = None
        for u in MOCK_USERS.values():
            if u['username'] == username or u['email'] == username:
                user = u
                break
        
        if not user:
            return jsonify(ErrorResponse.create(
                message="Invalid credentials"
            ).dict()), 401
        
        if not user['is_active']:
            return jsonify(ErrorResponse.create(
                message="Account is deactivated"
            ).dict()), 401
        
        # 验证密码
        if not password_manager.verify_password(password, user['password_hash']):
            return jsonify(ErrorResponse.create(
                message="Invalid credentials"
            ).dict()), 401
        
        # 更新最后登录时间
        user['last_login'] = datetime.utcnow()
        
        # 生成令牌
        access_token = jwt_auth.create_access_token(user['id'])
        refresh_token = jwt_auth.create_refresh_token(user['id'])
        
        # 返回用户信息和令牌
        user_response = user.copy()
        user_response.pop('password_hash')
        user_response['created_at'] = user_response['created_at'].isoformat()
        user_response['last_login'] = user_response['last_login'].isoformat()
        
        response = SuccessResponse.create(
            data={
                "user": user_response,
                "access_token": access_token,
                "refresh_token": refresh_token,
                "token_type": "Bearer"
            },
            message="Login successful"
        )
        return jsonify(response.dict())
    
    @app.route('/auth/refresh', methods=['POST'])
    def refresh_token():
        """刷新访问令牌"""
        data = request.get_json()
        refresh_token = data.get('refresh_token')
        
        if not refresh_token:
            return jsonify(ErrorResponse.create(
                message="Refresh token is required"
            ).dict()), 400
        
        if refresh_token in BLACKLISTED_TOKENS:
            return jsonify(ErrorResponse.create(
                message="Refresh token has been revoked"
            ).dict()), 401
        
        try:
            payload = jwt_auth.decode_token(refresh_token)
            user_id = payload.get('user_id')
            token_type = payload.get('type')
            
            if token_type != 'refresh' or not user_id or user_id not in MOCK_USERS:
                return jsonify(ErrorResponse.create(
                    message="Invalid refresh token"
                ).dict()), 401
            
            # 生成新的访问令牌
            new_access_token = jwt_auth.create_access_token(user_id)
            
            response = SuccessResponse.create(
                data={
                    "access_token": new_access_token,
                    "token_type": "Bearer"
                },
                message="Token refreshed successfully"
            )
            return jsonify(response.dict())
            
        except Exception as e:
            return jsonify(ErrorResponse.create(
                message=f"Token refresh failed: {str(e)}"
            ).dict()), 401
    
    @app.route('/auth/logout', methods=['POST'])
    @token_required
    def logout():
        """用户登出"""
        # 获取当前令牌
        auth_header = request.headers.get('Authorization')
        if auth_header:
            token = auth_header.split(' ')[1]
            BLACKLISTED_TOKENS.add(token)
        
        # 如果提供了刷新令牌，也将其加入黑名单
        data = request.get_json() or {}
        refresh_token = data.get('refresh_token')
        if refresh_token:
            BLACKLISTED_TOKENS.add(refresh_token)
        
        response = SuccessResponse.create(
            message="Logout successful"
        )
        return jsonify(response.dict())
    
    # ========================================================================
    # 用户信息和管理
    # ========================================================================
    
    @app.route('/auth/me', methods=['GET'])
    @token_required
    def get_current_user():
        """获取当前用户信息"""
        user_response = g.current_user.copy()
        user_response['created_at'] = user_response['created_at'].isoformat()
        if user_response['last_login']:
            user_response['last_login'] = user_response['last_login'].isoformat()
        
        response = SuccessResponse.create(
            data={"user": user_response},
            message="User information retrieved successfully"
        )
        return jsonify(response.dict())
    
    @app.route('/auth/change-password', methods=['POST'])
    @token_required
    def change_password():
        """修改密码"""
        data = request.get_json()
        current_password = data.get('current_password', '')
        new_password = data.get('new_password', '')
        
        if not current_password or not new_password:
            return jsonify(ErrorResponse.create(
                message="Current password and new password are required"
            ).dict()), 400
        
        if len(new_password) < 6:
            return jsonify(ErrorResponse.create(
                message="New password must be at least 6 characters long"
            ).dict()), 400
        
        user = MOCK_USERS[g.current_user['id']]
        
        # 验证当前密码
        if not password_manager.verify_password(current_password, user['password_hash']):
            return jsonify(ErrorResponse.create(
                message="Current password is incorrect"
            ).dict()), 400
        
        # 更新密码
        user['password_hash'] = password_manager.hash_password(new_password)
        
        response = SuccessResponse.create(
            message="Password changed successfully"
        )
        return jsonify(response.dict())
    
    @app.route('/auth/users', methods=['GET'])
    @permission_required('admin')
    def list_users():
        """获取用户列表（管理员权限）"""
        users = []
        for user in MOCK_USERS.values():
            user_data = user.copy()
            user_data.pop('password_hash')
            user_data['created_at'] = user_data['created_at'].isoformat()
            if user_data['last_login']:
                user_data['last_login'] = user_data['last_login'].isoformat()
            users.append(user_data)
        
        response = SuccessResponse.create(
            data={
                "users": users,
                "total": len(users)
            },
            message="Users retrieved successfully"
        )
        return jsonify(response.dict())
    
    @app.route('/auth/users/<int:user_id>', methods=['GET'])
    @permission_required('admin')
    def get_user(user_id: int):
        """获取指定用户信息（管理员权限）"""
        if user_id not in MOCK_USERS:
            return jsonify(ErrorResponse.create(
                message="User not found"
            ).dict()), 404
        
        user = MOCK_USERS[user_id].copy()
        user.pop('password_hash')
        user['created_at'] = user['created_at'].isoformat()
        if user['last_login']:
            user['last_login'] = user['last_login'].isoformat()
        
        response = SuccessResponse.create(
            data={"user": user},
            message="User information retrieved successfully"
        )
        return jsonify(response.dict())
    
    @app.route('/auth/users/<int:user_id>/deactivate', methods=['POST'])
    @permission_required('admin')
    def deactivate_user(user_id: int):
        """停用用户（管理员权限）"""
        if user_id not in MOCK_USERS:
            return jsonify(ErrorResponse.create(
                message="User not found"
            ).dict()), 404
        
        if user_id == g.current_user['id']:
            return jsonify(ErrorResponse.create(
                message="Cannot deactivate your own account"
            ).dict()), 400
        
        MOCK_USERS[user_id]['is_active'] = False
        
        response = SuccessResponse.create(
            message="User deactivated successfully"
        )
        return jsonify(response.dict())
    
    @app.route('/auth/users/<int:user_id>/activate', methods=['POST'])
    @permission_required('admin')
    def activate_user(user_id: int):
        """激活用户（管理员权限）"""
        if user_id not in MOCK_USERS:
            return jsonify(ErrorResponse.create(
                message="User not found"
            ).dict()), 404
        
        MOCK_USERS[user_id]['is_active'] = True
        
        response = SuccessResponse.create(
            message="User activated successfully"
        )
        return jsonify(response.dict())
    
    # ========================================================================
    # 权限测试端点
    # ========================================================================
    
    @app.route('/protected/read', methods=['GET'])
    @permission_required('read')
    def protected_read():
        """需要读权限的端点"""
        response = SuccessResponse.create(
            data={"message": "You have read permission"},
            message="Read operation successful"
        )
        return jsonify(response.dict())
    
    @app.route('/protected/write', methods=['POST'])
    @permission_required('write')
    def protected_write():
        """需要写权限的端点"""
        response = SuccessResponse.create(
            data={"message": "You have write permission"},
            message="Write operation successful"
        )
        return jsonify(response.dict())
    
    @app.route('/protected/delete', methods=['DELETE'])
    @permission_required('delete')
    def protected_delete():
        """需要删除权限的端点"""
        response = SuccessResponse.create(
            data={"message": "You have delete permission"},
            message="Delete operation successful"
        )
        return jsonify(response.dict())
    
    @app.route('/protected/admin', methods=['GET'])
    @role_required('admin')
    def admin_only():
        """仅管理员可访问的端点"""
        response = SuccessResponse.create(
            data={"message": "You are an administrator"},
            message="Admin operation successful"
        )
        return jsonify(response.dict())
    
    # ========================================================================
    # 应用信息
    # ========================================================================
    
    @app.route('/health', methods=['GET'])
    def health_check():
        """健康检查"""
        return jsonify({
            "status": "healthy",
            "timestamp": datetime.utcnow().isoformat()
        })
    
    @app.route('/info', methods=['GET'])
    def app_info():
        """应用信息"""
        return jsonify({
            "name": "Flask Auth Example",
            "version": "1.0.0",
            "description": "Flask认证功能示例应用",
            "endpoints": {
                "auth": [
                    "POST /auth/register",
                    "POST /auth/login",
                    "POST /auth/refresh",
                    "POST /auth/logout",
                    "GET  /auth/me",
                    "POST /auth/change-password"
                ],
                "admin": [
                    "GET  /auth/users",
                    "GET  /auth/users/<id>",
                    "POST /auth/users/<id>/deactivate",
                    "POST /auth/users/<id>/activate"
                ],
                "protected": [
                    "GET    /protected/read (需要read权限)",
                    "POST   /protected/write (需要write权限)",
                    "DELETE /protected/delete (需要delete权限)",
                    "GET    /protected/admin (需要admin角色)"
                ]
            },
            "default_users": {
                "admin": {"username": "admin", "password": "admin123", "role": "admin"},
                "user1": {"username": "user1", "password": "user123", "role": "user"},
                "viewer": {"username": "viewer", "password": "viewer123", "role": "viewer"}
            }
        })
    
    return app


if __name__ == '__main__':
    app = create_auth_app()
    
    print("=" * 60)
    print("Flask Auth Example")
    print("=" * 60)
    print("认证功能演示:")
    print("  用户认证:")
    print("    POST /auth/register - 用户注册")
    print("    POST /auth/login - 用户登录")
    print("    POST /auth/refresh - 刷新令牌")
    print("    POST /auth/logout - 用户登出")
    print("    GET  /auth/me - 获取当前用户信息")
    print("    POST /auth/change-password - 修改密码")
    print()
    print("  用户管理（管理员权限）:")
    print("    GET  /auth/users - 获取用户列表")
    print("    GET  /auth/users/<id> - 获取用户信息")
    print("    POST /auth/users/<id>/deactivate - 停用用户")
    print("    POST /auth/users/<id>/activate - 激活用户")
    print()
    print("  权限测试:")
    print("    GET    /protected/read - 需要read权限")
    print("    POST   /protected/write - 需要write权限")
    print("    DELETE /protected/delete - 需要delete权限")
    print("    GET    /protected/admin - 需要admin角色")
    print()
    print("  默认用户:")
    print("    admin/admin123 (管理员)")
    print("    user1/user123 (普通用户)")
    print("    viewer/viewer123 (只读用户)")
    print("=" * 60)
    
    app.run(
        host='0.0.0.0',
        port=5002,
        debug=True
    )